package br.com.lm.anotacoes;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * @author Robson Motta 
 * 
 * Representa todas as classes de um determinado pacote com as anotacoes atribuida
 */
public class AnnotationResult {
	
    //Argumento de pesquisa
    private Class<?> annotationClassArg; //Classe de Anotacao fornecida na pesquisa
    private String   packageClassArg;    //Pacote usado na pesquisa
    private String   originSearchArg;    //Origem da pesquisa 
	
    //Pacote da classe que recebeu a anotacao
    private String packageClasseAnnotated;
        
    //Resultado da pesquisa (Classe e seu Elemento de Anotacao)
    private Map<String, AnnotationElement> mapClasses;
	
    public AnnotationResult(){}

    /**
     * @return - HashMap( String classEncontradaPsq, AnnotationElement )  
     */
    public Map<String, AnnotationElement> getAllElements() {
        return mapClasses;
    }

    /**
     * @return - Array de String de todas as classes encontrada na pesquisa
     */
    public String[] getArrayClassesName() {
        return mapClasses == null ? null : (String[])mapClasses.keySet().toArray();
    }
        
    /**
     * @param className - Nome da classe que deseja as anotacoes 
     * @return - Elemento de anotacao contendo as ocorrencia(s) de anotacoes 
     */
    public AnnotationElement getElementByClassName(String className) {
	return mapClasses == null ? null : mapClasses.get(className);
    }
	
    /**
     * Filtra o resultado da pesquisa por pacote
     * @param packageName - Nome do pacote das classes pesquisadas/anotada
     * @return - HashMap( String classeAnotada, AnnotationElement )
     */
    public Map<String, AnnotationElement> getElementsByPackage(String packageName) {
        Map<String, AnnotationElement> result = null;
	if (mapClasses != null) {
            Set<Map.Entry<String, AnnotationElement>> set = mapClasses.entrySet();
            if (set != null) {
		for (Iterator<Map.Entry<String, AnnotationElement>> iter = set.iterator(); iter.hasNext();) {
                    Map.Entry<String, AnnotationElement> me = iter.next();
                    AnnotationElement element = me.getValue();
                    if (element.getPackageClassName().equals(packageName)) {
                        if (result == null)
                            result = new HashMap<String, AnnotationElement>();
			result.put(me.getKey(), element);
                    }
		}				
            }
        }
	return result;
    }
	
    /**
     * Filtra o resultado da pesquisa pela classe e anotacao
     * @param <T>        - Qualificacao para classe de anotacao
     * @param className  - Nome da classe que foi anotada 
     * @param annotation - Anotacao Ex: Entity.class
     * @return - Elemento de anotacao contendo as ocorrencia(s) de anotacao
     */
    public <T extends Annotation> AnnotationElement getElementByClsNameAndAnnotation(String className, Class<T> annotation) {
        AnnotationElement annoElementRetrn = null;
        if (mapClasses != null) {
            AnnotationElement annoElement = mapClasses.get(className);
            if (annoElement != null) {
                Set <AnnotationData> annoDatas = annoElement.getAnnotationByName(annotation.getSimpleName());
		if (annoDatas != null) {
                    annoElementRetrn = new AnnotationElement();
                    annoElementRetrn.setClassNameWithAnnotation(annoElement.getClassNameWithAnnotation());
                    annoElementRetrn.setPackageClassName(annoElement.getPackageClassName());
                    Map<String, Set<AnnotationData>> elements = new HashMap<String, Set<AnnotationData>>(1);
                    elements.put(annotation.getSimpleName(), annoDatas);
                    annoElementRetrn.setAllAnnotationData(elements);
		}
            }
	}
        return annoElementRetrn;
    }

    /**
     * Filtra o resultado da pesquisa pela classe e tipo de anotacao
     * @param className      - Nome da classe que foi anotada
     * @param annotationType - Tipo da anotacao (Ex: Class, Field, Constructor e Method)
     * @return - Elemento de anotacao contendo as ocorrencia(s) de anotacao
     */
    public AnnotationElement getElementByClsNameAndAnnotationType(String className, AnnotationType annotationType) {
	AnnotationElement annoElementRetrn = null;
	if (mapClasses != null) {
            AnnotationElement annoElement = mapClasses.get(className);
            if (annoElement != null) {
                Map<String, Set<AnnotationData>> mapAnno =  annoElement.getAnnotationByType(annotationType); 
		if (mapAnno != null) {
                    annoElementRetrn = new AnnotationElement();
                    annoElementRetrn.setClassNameWithAnnotation(annoElement.getClassNameWithAnnotation());
                    annoElementRetrn.setPackageClassName(annoElement.getPackageClassName());
                    annoElementRetrn.setAllAnnotationData(mapAnno);
		}
            }
	}
	return annoElementRetrn;
    }

    /**
     * Filtra o resultado da pesquisa pela classe, anotacao e tipo de anotacao
     * @param <T>        - Qualificacao para classe de anotacao
     * @param className  - Nome da classe que foi anotada
     * @param annotation - Anotacao Ex: Entity.class
     * @param annotationType - Tipo da anotacao (Ex: Class, Field, Constructor e Method)
     * @return - Elemento de anotacao contendo as ocorrencia(s) de anotacao
     */
    public <T extends Annotation> AnnotationElement getElementByGenericId(String className, Class<T> annotation, AnnotationType annotationType) {
	AnnotationElement annoElementRetrn = null;
	if (mapClasses != null) {
            AnnotationElement annoElement = mapClasses.get(className);
            if (annoElement != null) {
                 Set<AnnotationData> annoData = annoElement.getAnnotationByNameAndType(className, annotationType);
                 if (annoData != null) {
                     Map<String, Set<AnnotationData>> mapAnno = new HashMap<String, Set<AnnotationData>>(1);
                     mapAnno.put(className, annoData);
                     if (annoElementRetrn == null) 
                        annoElementRetrn = new AnnotationElement();
                     annoElementRetrn.setClassNameWithAnnotation(annoElement.getClassNameWithAnnotation());
                     annoElementRetrn.setPackageClassName(annoElement.getPackageClassName());
                     annoElementRetrn.setAllAnnotationData(mapAnno);    
                 }    
            }
	}	
	return annoElementRetrn;	
    }
	
    /**
     * Filtra o resultado da pesquisa pela chave completa da anotacao
     * @param <T>        - Qualificacao para classe de anotacao
     * @param className  - Nome da classe que que foi anotada
     * @param annotation - Anotacao Ex: Entity.class
     * @param annotationType - Tipo da anotacao (Ex: Class, Field, Constructor e Method)
     * @param annotationMember - Identificacao do method da anotacao (@interface) 
     * @return - Elemento de anotacao contendo as ocorrencia de anotacao
     */	
    public <T extends Annotation> AnnotationElement getElementById(String className, Class<T> annotation, AnnotationType annotationType, String annotationMember) {
	AnnotationElement annoElementRetrn = null;
	if (mapClasses != null) {
            AnnotationElement annoElement = mapClasses.get(className);
            if (annoElement != null) {
                Set<AnnotationData> annoData = annoElement.getAnnotationByNameTypeElementKey(annotationMember, annotationType, annotationMember);
		if (annoData != null) {
                    Map<String, Set<AnnotationData>> mapAnno = new HashMap<String, Set<AnnotationData>>(1);
                     mapAnno.put(className, annoData);
                     if (annoElementRetrn == null) 
                        annoElementRetrn = new AnnotationElement();
                     annoElementRetrn.setClassNameWithAnnotation(annoElement.getClassNameWithAnnotation());
                     annoElementRetrn.setPackageClassName(annoElement.getPackageClassName());
                     annoElementRetrn.setAllAnnotationData(mapAnno);    
		}
            }
         }
	return annoElementRetrn;	
    }
	
    public void setAllElements(Map<String, AnnotationElement> mapClasses) {
	this.mapClasses = mapClasses;   
    }
	        
    public Class<?> getAnnotationClassArg() {
	return annotationClassArg;
    }

    public void setAnnotationClassArg(Class<?> annotationClassArg) {
	this.annotationClassArg = annotationClassArg;
    }

    public String getOriginSearchArg() {
	return originSearchArg;
    }

    public void setOriginSearchArg(String originSearchArg) {
	this.originSearchArg = originSearchArg;
    }
	
    public String getPackageClassArg() {
	return packageClassArg;
    }

    public void setPackageClassArg(String packageClassArg) {
	this.packageClassArg = packageClassArg;
    }

    public String getPackageClasseAnnotated() {
        return packageClasseAnnotated;
    }

    public void setPackageClasseAnnotated(String packageClasseAnnotated) {
        this.packageClasseAnnotated = packageClasseAnnotated;
    }

    public Map<String, AnnotationElement> getMapClasses() {
        return mapClasses;
    }

    public void setMapClasses(Map<String, AnnotationElement> mapClasses) {
        this.mapClasses = mapClasses;
    }
    
}